package com.monolithiot.sso.interceptor;

import com.monolithiot.sso.common.context.Constants;
import com.monolithiot.sso.common.entity.User;
import com.monolithiot.sso.common.util.SessionAttributeUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * 2018/12/15 Create by 郭文梁
 * FrontAccessInterceptor
 * 前台用户访问拦截器
 *
 * @author 郭文梁
 * @date 2018/12/15
 */
@Slf4j
@Component
@ConfigurationProperties("interceptor.front")
public class FrontAccessInterceptor implements HandlerInterceptor {
    @Setter
    private Map<String, Boolean> grantedPath;
    @Setter
    private List<String> grantedReg;
    @Setter
    private String prefix;
    @Setter
    private String loginPath;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        User user = SessionAttributeUtil.fromSession(request.getSession(), User.class, Constants.Key.SESSION_USER);

        //用户是否已登录
        if (user != null) {
            return true;
        }

        String path = request.getRequestURI();

        //只拦截前台用户请求
        if (!path.startsWith(prefix)) {
            return true;
        }

        //检查是否需要授权
        if (isGranted(path)) {
            return true;
        }

        log.debug("Path {} is intercepted", path);

        try {
            response.sendRedirect(loginPath);
        } catch (IOException e) {
            log.error("Cannot send redirection command {}", e);
        }

        return false;
    }

    /**
     * 判断资源是否已授权
     *
     * @param path 路径
     * @return 是否已授权
     */
    private boolean isGranted(String path) {
        //由于yml中map的key的/会被忽略，加一个[]后正常
        String key = "[" + path + "]";
        Boolean granted = grantedPath.getOrDefault(key, false);
        if (!granted) {
            long count = grantedReg.stream().filter(path::matches).count();
            if (count > 0) {
                granted = true;
            }
        }
        return granted;
    }
}
